import {totalPage} from "../utils/NumberUtils.ts";
import {buildTopicInfo, type TopicInfo} from "./Topic.ts";
import {buildUserInfo, type UserInfo} from "./User.ts";
import {buildMedalInfo, type ItemTypeInfo} from "./Item.ts";
import {AnonymousUtils} from "../utils/AnonymousUtils.ts";
import {buildReplyInfo, type ReplyInfo} from "./Reply.ts";

export interface CustomLevel {
    rank: number
    name: string
}

export interface RepliesData {
    levels: CustomLevel[] | undefined
    topic: TopicInfo
    userMap: UserMap
    replies: ReplyInfo[]
}

export interface TopicDetailDataRaw {
    __F: {
        name: string
        custom_level: string | undefined
    }
    __T: any
    __R: ReplyInfoRaw[]
    __U: any
    __PAGE: number
    __ROWS: number
    totalPage: number
    __R__ROWS_PAGE: number
}

export interface ReplyInfoRaw {
    14: { [key: number]: number } | undefined
    lou: number
    pid: number
    tid: number
    type: number
    authorid: number
    postdatetimestamp: number
    from_client: string
    postdate: string
    subject: string
    content: string
    alterinfo: string
    vote: string
    attachs: any
    hotreply: any
    hotreply_id: any
    comment: any
    comment_id: any
    org_fid: number | undefined
    js_escap_org_forum: string | undefined


    recommend: number
    score: number
    score_2: number
}

const parseParameters = (text: string | undefined): string[] | undefined => {
    if (!text) return undefined
    const t1 = text.replace(/["'](.*?)["']/g, (substring) => substring.replace(/,/g, "||"));
    return t1.split(',')
        .map(i => i.replace(/['"]/g, "").trim())
        .map(i => i.replace(/\|\|/g, ","))
        .map(i => i === 'null' ? "" : i)
}
const parseGifts = (text: string): { [key: number]: number } | undefined => {
    if (!text || text === '0') return undefined
    const arr = text.split(',');
    const gifts: { [key: number]: number } = {}
    for (let i = 0; i < arr.length; i += 2) {
        gifts[Number(arr[i])] = Number(arr[i + 1])
    }
    return gifts
}

const clearLineBreak = (text: string): string => text.replace(/\n/g, "").replace(/\r/g, "")

const handleContent = (e: Element): string => {
    const newHtml = e.innerHTML.replace(/<br\/*>/g, "{换行}");
    return newHtml.replace(/\{换行}/g, "<br/>")
}

const scriptMatcher = (e: Element, id: string, regExp: RegExp) => {
    const scriptTag = e.querySelector("#" + id)?.nextElementSibling;
    if (scriptTag && scriptTag.tagName === 'SCRIPT') {
        const script = clearLineBreak(scriptTag.innerHTML)
        return regExp.exec(script)?.[1]
    }
    return undefined
}

const parseReplies = (doc: Document, tid: number): ReplyInfoRaw[] => {
    const res: ReplyInfoRaw[] = []
    // 回复的 <table> 标签
    const replyTables = doc.getElementsByClassName("forumbox postbox")
    for (const replyTable of replyTables) {
        const reply = {tid} as ReplyInfoRaw
        const scriptTag = replyTable.nextElementSibling;
        if (scriptTag && scriptTag.tagName === "SCRIPT") {
            const script = clearLineBreak(scriptTag.innerHTML)
            // console.log('script', script)
            const params = /commonui\.postArg\.proc\((.+)\)if/.exec(script)?.[1]
            const paramsArray = parseParameters(params)?.filter(i => !i.startsWith('$'))
            if (paramsArray) {
                // console.log('paramsArray', paramsArray)

                reply.lou = Number(paramsArray[0])
                reply.pid = Number(paramsArray[3])
                reply.type = Number(paramsArray[4])
                reply.authorid = Number(paramsArray[6])
                reply.postdatetimestamp = Number(paramsArray[7])
                reply.from_client = paramsArray[12]

                const [n1, n2, n3] = paramsArray[8].split(",").map(Number)
                reply.recommend = n1
                reply.score = n2
                reply.score_2 = n3

                reply[14] = parseGifts(paramsArray[15])

                if (!isNaN(reply.lou)) {
                    reply.postdate = doc.getElementById(`postdate${reply.lou}`)?.innerText || ""
                    reply.subject = doc.getElementById(`postsubject${reply.lou}`)?.innerText || ""
                    reply.content = handleContent(doc.getElementById(`postcontent${reply.lou}`)!)

                    reply.vote = parseParameters(scriptMatcher(replyTable, `votec${reply.lou}`, /commonui\.vote\((.+)\)/))?.[2] || ""
                    reply.alterinfo = parseParameters(scriptMatcher(replyTable, `alertc${reply.lou}`, /commonui\.loadAlertInfo\((.+)\)/))?.[0] || ""

                    const attach = scriptMatcher(replyTable, `postattach${reply.lou}`, /attach\.load.+?(\[.+?])/)
                        ?.replace(/([,{])(\w+?):/g, "$1\"$2\":")?.replace(/'/g, "\"")
                    reply.attachs = attach ? JSON.parse(attach) : undefined
                }
                //     todo 贴条
                //     todo 热评
            }
        }
        // console.log(reply)
        res.push(reply)
    }

    return res
}

export const parseTopic = (htmlString: string, doc: Document): any => {
    const paramString = /commonui\.postArg\.setDefault\((.+)\)/.exec(htmlString)?.[1];
    const parameters = parseParameters(paramString)?.map(i => /^-*\d+$/.exec(i) ? Number(i) : i) || []
    // console.log('parameters', parameters)

    return {
        fid: parameters[0],
        tid: parameters[2],
        type: parameters[10],
        subject: doc.getElementById('currentTopicName')?.innerText,
        post_misc_var: {16: parameters[5], vote: parameters[8]},
        topic_misc_var: {1: parameters[4]},
        __ST: parameters[1] ? {
            tid: parameters[1],
            subject: doc.getElementById('currentSetName')?.innerText
        } : undefined
    }
}


export const buildTopicDetailDataRaw = (htmlString: string): TopicDetailDataRaw => {
    const doc = new DOMParser().parseFromString(htmlString, 'text/html')

    const topicId = Number(/__CURRENT_TID=(\d+)/.exec(htmlString)?.[1])
    const pageString = /__PAGE = (\{.+})/.exec(htmlString)?.[1]?.replace(/(\d):/g, "'$1':").replace(/'/g, "\"")
    const pageData = pageString ? JSON.parse(pageString) : undefined
    const page = pageData && pageData[2] || 1
    const size = pageData && pageData[3] || 20
    const totalPage = pageData && pageData[1] || 1
    // 猜测最小楼层数
    const minFloor = (totalPage - 1) * size + 1

    const userString = /commonui\.userInfo\.setAll\((.+)\)/.exec(htmlString)?.[1]!!.trim()

    const replies = parseReplies(doc, topicId)
    const maxFloor = replies.map(i => i.lou).sort((a, b) => b - a)[0];
    return {
        __F: {
            name: doc.getElementById('currentForumName')?.innerText || "",
            custom_level: /__CUSTOM_LEVEL=(.+)/.exec(htmlString)?.[1]
        },
        __T: parseTopic(htmlString, doc),
        __R: replies,
        __U: userString ? JSON.parse(userString) : {},
        __PAGE: page,
        __ROWS: maxFloor ? Math.max(maxFloor + 1, minFloor) : minFloor,
        totalPage: totalPage,
        __R__ROWS_PAGE: size,
    }
}

export interface TopicDetailData extends RepliesData {
    forumName: string
    page: number
    size: number
    total: number
    totalPage: number
}


export const buildTopicDetailData = (data: TopicDetailDataRaw): TopicDetailData => {
    // console.log(data)

    const levels = data.__F.custom_level ? JSON.parse(data.__F.custom_level.replace(/\{r:/g, "{\"rank\":").replace(/,n:/g, ",\"name\":")).reverse() : undefined

    const userMap = buildUserMap(data.__U)

    const topic = buildTopicInfo(data.__T)
    const replies = data.__R.map(buildReplyInfo)
    // 设置回复是否为本帖内被处罚
    replies.forEach(i => i.punished = topic.punishedUserId?.includes(Number(i.authorUid)))

    const res: TopicDetailData = {
        forumName: data.__F.name,
        levels,
        page: data.__PAGE,
        size: data.__R__ROWS_PAGE,
        total: data.__ROWS,
        totalPage: data.totalPage || totalPage(data.__ROWS, data.__R__ROWS_PAGE),
        topic, userMap, replies
    }
    return res
}

export interface UserMap {
    users: {
        [key: number]: UserInfo
    }
    groups: {
        [key: number]: string
    }
    anonymous: {
        [key: number]: string
    }
    medals: ItemTypeInfo[]
    reputationId: number | undefined
}

export const buildUserMap = (data: any): UserMap => {
    const map: UserMap = {
        users: {},
        groups: {},
        anonymous: {},
        medals: [],
        reputationId: undefined,
    }
    for (const key in data) {
        const item = data[key]
        if (key === '__GROUPS') {
            Object.values(item).forEach((v: any) => {
                map.groups[v[2]] = v[0]
            })
        } else if (key === '__MEDALS') {
            map.medals = Object.values(item).map((i: any) => buildMedalInfo(i))
        } else if (key === '__REPUTATIONS') {
            map.reputationId = Object.keys(item)[0] ? Number(Object.keys(item)[0]) : undefined
        } else {
            // 用户信息
            const userId = Number(key)
            if (userId < 0) {
                map.anonymous[userId] = AnonymousUtils.transform(item.username)!!
            } else {
                if (item.buffs) {
                    item.buffs = Object.values(item.buffs).flatMap(i => i)
                }
                map.users[userId] = buildUserInfo(item)
            }
        }
    }
    return map
}